// raymarcher.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "EasyBMP.h"

#include "raymarching.h"
#include "Scene.h"
#include "Perlin.h"

void SetPixel(BMP & bmp, uint x, uint y, XMVECTOR pixel)
{
	RGBApixel p;
	p.Red = pixel.m128_f32[0] * 255;
	p.Green = pixel.m128_f32[1] * 255;
	p.Blue = pixel.m128_f32[2] * 255;
	p.Alpha = pixel.m128_f32[2] * 255;
	bmp.SetPixel(x, y, p);
}

struct Camera
{
	XMVECTOR Position;
	float RotationX;
	float RotationY;
	float FOVRad;
};

struct RayGenData
{
	XMVECTOR StartingRayDir;
	XMVECTOR RayDirDeltaX;
	XMVECTOR RayDirDeltaY;
};

void getRayGenData(const Camera & camera, float resX, float resY, RayGenData & outData)
{
	XMVECTOR direction = XMVectorSet(
		cosf(camera.RotationX) * cosf(camera.RotationY),
		sinf(camera.RotationX),
		cosf(camera.RotationX) * sinf(camera.RotationY),
		0);

	direction = XMVector3Normalize(direction);

	XMVECTOR right = XMVector3Cross(XMVectorSet(0, 1, 0, 0), direction);
	right = XMVector3Normalize(right);
	XMVECTOR up = XMVector3Cross(direction, right);

	float projWid = 1.0f * tanf(camera.FOVRad * 0.5f);
	float projHei = projWid * resY / resX;
	float step = projWid / resX;

	outData.StartingRayDir = direction - right * projWid * 0.5f + up * projHei * 0.5f;

	outData.RayDirDeltaX = right * step;
	outData.RayDirDeltaY = - up * step;
}

void constructScene(Scene & scene)
{
	PointLight pl;
	scene.PointLights.push_back(pl);
}


int main(int argc, char * argv[])
{
	srand(time(NULL));

	int resX = 320, resY = 240;
	float camPosX = -6.0f;
	float camPosY = 0.25f;
	float camPosZ = 2.0f;
	float camRotX = 0;
	float camRotY = 0;


	for(size_t i = 1; i < argc; i++)
	{
		if(strcmp(argv[i], "/r") == 0)
		{
			resX = atoi(argv[i+1]);
			resY = atoi(argv[i+2]);
			i += 2;
		}
		else if(strcmp(argv[i], "/cp") == 0)
		{
			camPosX = atof(argv[i+1]);
			camPosY = atof(argv[i+2]);
			camPosZ = atof(argv[i+3]);
			i += 3;
		}
		else if(strcmp(argv[i], "/cr") == 0)
		{
			camRotX = atof(argv[i+1]);
			camRotY = atof(argv[i+2]);
			i += 2;
		}
	}

	
	const char * filename = "render.bmp";

	BMP bmp;
	bmp.SetSize(resX, resY);

	Scene scene;
	constructScene(scene);

	Camera camera;
	camera.Position = XMVectorSet(camPosX, camPosY, camPosZ, 0);
	camera.RotationX = camRotX;
	camera.RotationY = camRotY;
	camera.FOVRad = PI * 0.5f;

	RayGenData rayGenData;
	getRayGenData(camera, resX, resY, rayGenData);

	XMVECTOR currRowStartRayDir = rayGenData.StartingRayDir;
	XMVECTOR currRayDir;

	perlin::debugMaxPer = 0;
	perlin::debugMinPer = 0;

	for(uint y = 0; y < resY; y++)
	{
		currRayDir = currRowStartRayDir;
		for(uint x = 0; x < resX; x++)
		{
			Ray ray(camera.Position, XMVector3Normalize(currRayDir));
			XMVECTOR pixelColor = raymarch(ray, scene, x, y);

			SetPixel(bmp, x, y, pixelColor);

			currRayDir += rayGenData.RayDirDeltaX;
		}
		currRowStartRayDir += rayGenData.RayDirDeltaY;

		printf("\rDone %d %%", int(100.0f*((float)y/(float)resY)));

	}

	printf("Max per: %f, Min per: %f\n", perlin::debugMaxPer, perlin::debugMinPer);

	bmp.WriteToFile(filename);
	return 0;
}

